package com.wecon.springdemo.config;

import com.wecon.springdemo.shiro.UserRealm;
import com.wecon.springdemo.shiro.UserSessionManager;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Weikang  Wang
 * @program: com.wecon.springdemo.config
 * @description: shiro配置类
 * subject ----- securityManager ----- realm
 * @date 2020/4/28 10:04
 */
@Configuration
public class ShiroConfig {

    /**
     * 过滤器
     *
     * @return /
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        /* 跳转到登录页面
         * 未登录接口，前后端分离时，这里跳转到一个需要登录的提示接口
         */
        shiroFilterFactoryBean.setLoginUrl("/shiro/login");

        // 前后端分离时，不需要这个调用
        shiroFilterFactoryBean.setSuccessUrl("/shiro/index");

        // 登录后没有权限跳转的接口
        shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/unAuth");

        /*
         * org.apache.shiro.web.filter.mgt ---> DefaultFilter
         * shiro内置过滤器：可以实现权限相关的拦截器
         *      常用过滤器：
         *          1.anon：无需认证就可访问
         *          2.authc：必须认证才可访问
         *          3.user：remember me之后可以访问
         *          4.perms：（资源权限）如果当前用户具有权限，则允许访问的过滤器；如果用户没有指定所有权限，则拒绝访问的过滤器。
         *          5.roles：（角色权限）如果当前用户具有角色，则允许访问的过滤器；如果用户没有指定所有角色，则拒绝访问的过滤器。
         *          6.logout：接收到请求后，将立即注销当前subject，然后重定向到已配置的redirectUrl。
         * 使用LinkedHashMap的话，是有序的，保持顺序性，不会导致拦截时有时无的坑（如果用HashMap的话就是坑了）
         */

        // 使用LinkedHashMap的话，是有序的，保持顺序性，不会导致拦截时有时无的坑（如果用HashMap的话就是坑了）
        Map<String, String> filterMap = new LinkedHashMap<>();

        // 可以直接访问
        filterMap.put("/shiro/everyone", "anon");
        filterMap.put("/template", "anon");
        filterMap.put("/shiro/login", "anon");
        filterMap.put("/shiro/doLogin", "anon");
        filterMap.put("/test/**", "anon");
//        filterMap.put("/publicMethod/**", "anon");

        // shiro/ 这个路径下的必须都得登录才能访问
//        filterMap.put("/shiro/**", "authc");

        // 退出过滤器(可不做)
        filterMap.put("/logout", "logout");

        // 符合角色可以访问
        filterMap.put("/admin/**", "roles[admin]");

        // 拥有权限可以访问
        filterMap.put("/shiro/add", "perms[add]");
        filterMap.put("/shiro/update", "perms[update]");

        // 全局控制访问，一般在最后配置
        filterMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;
    }

    /**
     * 安全管理器
     *
     * @return securityManager
     */
    @Bean(value = "securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 前后端分离需要设置sessionManager
        securityManager.setSessionManager(getSessionManager());

        /*
         * 调用getRealm进行装载，直接new是不行的，因为执行顺序有问题
         * setRealm放在最下面，不然某些时候会不生效
         */
        securityManager.setRealm(getRealm());

        return securityManager;
    }

    /**
     * 创建realm
     *
     * @return userRealm
     */
    @Bean(value = "getRealm")
    public UserRealm getRealm() {
        return new UserRealm();
    }


    /**
     * 开启shiro注解功能
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * session管理器，前后端分离使用
     *
     * @return sessionManager
     */
    @Bean
    public SessionManager getSessionManager() {

        UserSessionManager sessionManager = new UserSessionManager();

        // 设置session过期时间（单位：毫秒）(默认30分钟)，时长内不操作则过期
//        sessionManager.setGlobalSessionTimeout(20000);

        return sessionManager;
    }

}
